﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NoteExpress;
using System.IO;
using System.Xml.Linq;
using System.Xml;

namespace NoteExpress
{
    public static class NE2Extension
    {
        public static string[][] GetPath(this INEItem item)
        {
            var str = item.Folders;
            var tmp1 = str.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            var rt = new string[tmp1.Length][];
            for (int i = 0; i < tmp1.Length; i++)
            {
                rt[i] = tmp1[i].Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
            }
            return rt;
        }

        public static INEItem[] GetItems(this INEFolder folder)
        {
            var rt = new List<INEItem>();
            for (var i = 0; i < folder.Items.Count; i++)
                rt.Add(folder.Items.get_Items(i));
            return rt.ToArray();
        }

        //static Stack<INEFolder> FolderHistory = new Stack<INEFolder>();
        //public static INEFolder GetActiveFolder(this INELibraries lib)
        //{
        //    if (FolderHistory.Count<1 || FolderHistory.Peek().ToNEFolder().UID != lib.ActiveFolder.UID)
        //    FolderHistory.Push(lib.ActiveFolder);
        //    return FolderHistory.Peek();
        //}
        //public static void ActiveteInHistory(this INEFolder folder)
        //{
        //    if (FolderHistory.Count < 1 || FolderHistory.Peek().ToNEFolder().UID != folder.UID)
        //        FolderHistory.Push(folder);
        //    folder.Activate();
        //}
        //public static INEFolder GetBackFolder(this INELibraries lib)
        //{
        //    if (FolderHistory.Peek().ToNEFolder().UID == lib.ActiveFolder.UID) return lib.ActiveFolder;
        //    if(FolderHistory.Count > 0)
        //        return FolderHistory.Pop();
        //    else return lib.ActiveFolder;
        //}

        public static INEItem[] AddUnique(this INEItem[] s1, INEItem[] s2)
        {
            var tmp = new Dictionary<string, INEItem>();
            foreach (var t in s1)
            {
                tmp.Add(t.UID, t);
            }
            foreach (var t in s2)
            {
                if (tmp.Keys.Contains(t.UID)) continue;
                else
                    tmp.Add(t.UID, t);
            }
            return tmp.Values.ToArray();
        }

        public static INEItem[] Mutual(this INEItem[] s1, INEItem[] s2)
        {
            var tmp = new List<string>();
            var rt = new List<INEItem>();
            foreach (var t in s1)
            {
                tmp.Add(t.UID);
            }
            foreach (var t in s2)
            {
                if (tmp.Contains(t.UID)) rt.Add(t);
            }
            return rt.ToArray();
        }

        public static string[] GetPaths(this INEItem item)
        {
            if (item.Folders == null) throw new ApplicationException(item.Values["Title"] + " haven't any folders.");
            var rt= item.Folders.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            var rt2 = new string[rt.Length];
            for(var i=0;i<rt.Length;i++)
            {
                rt2[i] = "\\References" + rt[i];
            }
            return rt2;
        }

        public static INEFolder ToNEFolder(this string folder)
        {
            var ne = new NEHost2();
            if (folder == string.Empty) return ne.Libraries.ActiveFolder.ParentLibrary.References;
            var path = folder.Split(new Char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
            var i0 = 0;
            if (path[0] == "References") i0 = 1;


            var tt=ne.Libraries.ActiveFolder.ParentLibrary.References;
            for (var t = i0; t < path.Length;t++ )
            {
                var i = tt.IndexOf(path[t]);
                if (i < 0) return null;
                tt = tt.Children[i];
            }
            return tt;
        }

        public static INEFolder ToNEFolder(this string folder,INEHost2 ne)
        {
            var path = folder.Split(new Char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
            var tt = ne.Libraries.ActiveFolder.ParentLibrary.References;
            foreach (var t in path)
            {
                var i = tt.IndexOf(t);
                tt = tt.Children[i];
            }
            return tt;
        }

        public static INEFolder[] GetFolders(this INEItem item)
        {
            var rt=new List<INEFolder>();
            foreach(var t in item.Folders.Split(new string[]{"\r\n"},StringSplitOptions.RemoveEmptyEntries))
            {
                rt.Add(t.ToNEFolder());
            }
            return rt.ToArray();
        }

        public static INEFolder[] GetParents(this INEFolder folder)
        {
            if (folder.UID == folder.ParentLibrary.References.UID)
                return null;

            var rt = new List<INEFolder>();
            INEFolder tmp=folder.Parent;
            do
            {
                if (tmp.Title == "Searches") break;
                if (tmp.UID != folder.ParentLibrary.References.UID )
                    rt.Add(tmp);
                else
                {
                    rt.Add(folder.ParentLibrary.References);
                    break;
                }
                tmp = tmp.Parent;
            } while (true);
            rt.Reverse();
            return rt.ToArray();
        }

        static void GetItems(INEFolder folder, List<INEItem> arr)
        {
            for (var i = 0; i < folder.Items.Count; i++)
            {
                var tmp = folder.Items.get_Items(i);
                if (tmp.Values["Title"].IndexOf("{") == 0) continue;
                if (arr.Find(tmp) == null)
                arr.Add(tmp);
            }
            for (var i = 0; i < folder.Count; i++)
            {
                var tmp = folder.Children[i];
                if (tmp.Title.IndexOf("*") == 0) continue;

                GetItems(tmp, arr);
            }
        }

        public static INEItem[] GetItems(this INEFolder folder, bool Iteration = true)
        {
            var arr = new List<INEItem>();
            if (Iteration)
            {
                for (var i = 0; i < folder.Items.Count; i++)
                {
                    var tmp = folder.Items.get_Items(i);
                    if (tmp.Values["Title"].IndexOf("{") == 0) continue;
                    if(arr.Find(tmp)==null)
                    arr.Add(tmp);
                }
            }
            else
                GetItems(folder, arr);

            return arr.ToArray();
        }

        public static string GetFullName(this INEFolder folder)
        {
            var list = folder.GetParents();
            if (list == null) return folder.Title+"\\";
            string str = "\\";
            foreach (var t in list)
            {
                str +=  t.Title+"\\" ;
            }
            return str+folder.Title+"\\";
        }

        public static INEFolder GetNeFolder(string fullname)
        {
            var ne = new NEHost2();
            var folder = ne.Libraries.ActiveFolder.ParentLibrary.References;
            var list = fullname.Split('\\');
            foreach (var t in list)
            {
                folder = folder.get_Children(folder.IndexOf(t));
            }
            return folder;
        }

        public static int GetLayer(this INEFolder folder)
        {
            var list = new List<INEFolder>();
            var tmp = folder;
            while (tmp.Parent != null)
            {
                list.Add(tmp);
                tmp = tmp.Parent;
            }
            return list.Count;
        }

        public static INEFolder GetParent(this INEFolder folder, int layer)
        {
            var list = new List<INEFolder>();
            var tmp = folder;
            while (tmp.Parent != null)
            {
                list.Add(tmp);
                tmp = tmp.Parent;
            }
            list.Reverse();
            return list[layer];
        }

        public static string GetID(this INEFolder folder)
        {
            var pos = folder.UID.IndexOf("_");
            return folder.UID.Substring(pos + 1, folder.UID.Length - pos - 1);
        }

        public static string GetFolderID(this INELibrary lib, string id)
        {
            return lib.UID + "_" + id;
        }

        public static INEItem GetRelatedItem(this INEFolder folder)
        {
            if (folder.FolderType != INEFolderType.INEFolderType_Ref )
                return null;
            INEItem item = null;
            for (var i = 0; i < folder.Items.Count; i++)
            {
                if (folder.UID == (folder.ParentLibrary.UID+"_"+ folder.Items.Items[i].Values["Place Published"]))
                {
                    item=folder.Items.Items[i];
                    item.set_Values("Title", "{" + folder.GetFullName() + "}");
                   
                    break;
                }
            }
            if (item == null)
            {
                item = folder.NewItem();
                item.set_Values("Title", "{"+folder.GetFullName()+"}");
                item.set_Values("Year", "10000");
                var pos=folder.UID.IndexOf("_");
                var tmp=folder.UID.Substring(pos+1,folder.UID.Length-pos-1);
                item.set_Values("Place Published", tmp);
                folder.AddItem(item);
            }
            return item;
        }

        public static INEFolder GetSubFolder(this INEFolder folder, string name)
        {
            var i = folder.IndexOf(name);
            if (i > -1) return folder.Children[i];
            return null;
        }

        public static INEFolder GetRelatedFolder(this INEItem item)
        {
            var ne = new NoteExpress.NEHost2();
            var tmp = ne.Libraries.ActiveFolder;
            var Root = ne.Libraries.ActiveFolder.ParentLibrary.References.GetSubFolder("@Relation");
            if(Root==null)
                Root = ne.Libraries.ActiveFolder.ParentLibrary.References.Add("@Relation");
            tmp.Activate();
            return Root.GetSubFolder(item.Values["ID"]);
        }

        public static INEFolder CreateRelatedFolder(this INEItem item)
        {
            
            var ne = new NoteExpress.NEHost2();
            var t0 = ne.Libraries.ActiveFolder;
            var Root = ne.Libraries.ActiveFolder.ParentLibrary.References.GetSubFolder("@Relation");
            if (Root == null)
                Root = ne.Libraries.ActiveFolder.ParentLibrary.References.Add("@Relation");
            var tmp= Root.GetSubFolder(item.Values["ID"]);
            if (tmp == null)
            {
                tmp = Root.Add(item.Values["ID"]);
                tmp.Add("Citing");
                tmp.Add("Cited");
            }
            tmp.LinkItem(item);
            t0.Activate();
            return tmp;
        }

        public static INEItem[] GetCiting(this INEItem item)
        {
            var tmp = item.GetRelatedFolder();
            if (tmp == null)
                return null;
            var folder=tmp.GetSubFolder("Citing");
            var rt=new List<INEItem>();
            for (var i = 0; i < folder.Items.Count; i++)
            {
                var t = folder.Items.Items[i];
                if (t.Values["Title"]!=null && t.Values["Title"].IndexOf("{") == 0) continue;
                rt.Add(t);
            }
            return rt.ToArray();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="item">主文献</param>
        /// <param name="refItem">引用的文献</param>
        /// <returns></returns>
        public static string GetCtingNo(this INEItem item,INEItem refItem)
        {
            var id=item.Values["ID"];
            var str = refItem.Values["Custom 1"];

            XDocument doc;

            if (str != null && str.IndexOf("<") < 0)
            {
                if (System.Windows.Forms.DialogResult.Yes == System.Windows.Forms.MessageBox.Show(refItem.Values["ID"] + ": Custom 1 被占用！是否清空", "警告", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question))
                    refItem.Values["Custom 1"] = "";
                return string.Empty;
            }

            var ser = new System.Xml.Serialization.XmlSerializer(typeof(SerializableDictionary<string, string>));
            StringBuilder sb = new StringBuilder();
            SerializableDictionary<string, string> list;

            if (str != null && str != string.Empty)
            {
                doc = XDocument.Parse(str);
                if(doc.Root.Element("Reference")!=null)
                {
                    var sr = new StringReader(doc.Root.Element("Reference").Elements().First().ToString());
                list = (SerializableDictionary<string, string>)ser.Deserialize(sr);
                if (list.Keys.Contains(id))
                    return list[id];
                }
            }
            return string.Empty;
        }
        public static INEItem[] GetCited(this INEItem item)
        {
            var tmp = item.GetRelatedFolder();
            if (tmp == null)
                return null;
            var folder = tmp.GetSubFolder("Cited");
            var rt = new List<INEItem>();
            for (var i = 0; i < folder.Items.Count; i++)
            {
                var t = folder.Items.Items[i];
                if (t.Values["Title"].IndexOf("{") == 0) continue;
                rt.Add(t);
            }
            return rt.ToArray();
        }
        /// <summary>
        /// 获取被当前文献引用的文献集
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public static INEItem[] GetCitingFromPaths(this INEItem item)
        {
            var rt = new List<INEItem>();
            foreach (var t in item.Folders.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
            {
                if (t.IndexOf("\\@Relation") > -1)
                {
                    var folder = t.ToNEFolder();
                    try
                    {
                        rt.Add(folder.Parent.Items.get_Items(0));
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(folder.Parent.Title+" have no items!",ex);
                    }
                }
            }
            return rt.ToArray();
        }
        /// <summary>
        /// 获取引用当前文献的文献集
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public static INEItem[] GetCitedFromPaths(this INEItem item)
        {
            var rt = new List<INEItem>();
            foreach (var t in item.Folders.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
            {
                if (t.IndexOf("\\@Relation") > -1 && t.IndexOf("Citing") > -1)
                {
                    var folder = t.ToNEFolder();
                    try
                    {
                        rt.Add(folder.Parent.Items.get_Items(0));
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(folder.Title + " has no items!", ex);
                    }

                }
            }
            return rt.ToArray();
        }

        public static INEItem FirstItem(this INEItems items)
        {
            if (items.Count < 1)
                throw new Exception("There is no item.");

            return items.get_Items(0);
        }

        public static INEItem FindItemFromID(this INEFolder folder, string ID)
        {
            var items=folder.Items;
            for (var i = 0; i < items.Count; i++)
            {
                var tmp=items.get_Items(i);
                if (tmp.Values["ID"] == ID)
                    return tmp;

            }
            return null;
        }
        public static INEItem FindItemFromUID(this INEFolder folder, string UID)
        {
            var items = folder.Items;
            for (var i = 0; i < items.Count; i++)
            {
                var tmp = items.get_Items(i);
                if (tmp.UID == UID)
                    return tmp;

            }
            return null;
        }
        public enum AttachFileType { All, Pdf, Doc };
        public static void OpenAttachFile(this INEItem item, AttachFileType type=AttachFileType.All)
        {
            var tmp = item.AttachmentFiles;
            if (tmp == null) return;
            foreach (var t in tmp.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
            {
                System.Diagnostics.Process.Start(t);
            }
        }

        public static INEItem ToNEItem(this object obj)
        {
            return obj as INEItem;
        }

        public static INEFolder ToNEFolder(this object obj)
        {
            return obj as INEFolder;
        }


        public static INEItem Find(this IEnumerable<INEItem> items,INEItem UID)
        {
            foreach (var t in items)
            {
                if(t.UID==UID.UID) return t;
            }
            return null;
        }


        public static XDocument GetCustom(this INEItem item)
        {
            var str = item.Values["Custom 1"];

            XDocument xmldoc = null;
            if (str != null && str != string.Empty)
            {
                xmldoc = XDocument.Parse(str);
            }
            else
                xmldoc = new XDocument(new XDeclaration("1.0", "utf-16", "yes"), new XElement("NEUT"));

            return xmldoc;
        }

        public static void SaveCustom(this INEItem item, XDocument doc)
        {
            var sb = new StringBuilder();
            var tr = new StringWriter(sb);
            doc.Save(tr);

            item.Values["Custom 1"] = sb.ToString();
        }

        public static INEItems ToItems(this INEItem[] items)
        {
            var ne = new NoteExpress.NEHost2();
            var tmp = ne.Libraries.ActiveFolder.NewItems();
            foreach (var t in items)
                tmp.Add(t);
            return tmp;
        }

        public static INEItem[] ToArray(this INEItems items)
        {
            var tmp = new INEItem[items.Count];
            var ne = new NoteExpress.NEHost2();
            for (var i = 0; i < tmp.Length; i++)
                tmp[i] = items.get_Items(i);
            return tmp;
        }

        public static INEFolder GetFolder2(this INEFolder folder, string title)
        {
            var tmp = folder.GetSubFolder(title);
            if (tmp == null)
            {
                var t0 = folder.ParentLibrary.Parent.ActiveFolder;
                tmp = folder.Add(title);
                t0.Activate();
            }
            return tmp;
        }

        public static INEFolder FolderItemGo(this INEItem item)
        {
            var str=item.get_Values("Title");
            if (str.IndexOf("{") != 0) return null;

            str = str.Substring(1, str.Length - 2);
            var folder = str.ToNEFolder();

            return folder;
        }
        
        public static readonly string[] NEItemField =
        {
            "Title",
"Reference Type",
"Star",
"Tag",
"Author",
"Year",
"Short Title",
"Abstract",
"Memo",
"URL",
"Secondary Author",
"Secondary Title",
"Place Published",
"Publisher",
"Journal",
"Volume",
"Issue",
"Number of Volumes",
"Number",
"Pages",
"Number of Words",
"Price",
"Section",
"Tertiary Author",
"Tertiary Title",
"Cited Count",
"Num of Bibliographies",
"Bibliographies",
"Edition",
"DOI",
"Date Displayed",
"Date",
"Type of Work",
"Subsidiary Author",
"Alternate Title",
"ISBN/ISSN",
"Original Publication",
"Reprint Edition",
"Reviewed Item",
"Accession Number",
"Call Number",
"Category",
"Label",
"BibTeX Key",
"Keywords",
"Subject Headings",
"Subject",
"Image",
"Author Affiliation",
"Author Address",
"Caption",
"Translated Author",
"Translated Title",
"Database Provider",
"Database Updated",
"Language",
"Country",
"Date Accessed",
"Date Created",
"Date Modified",
"Custom 1",
"Custom 2",
"Custom 3",
"Custom 4",
"Custom 5",
"Custom Integer 1",
"Custom Integer 2",
"Custom Integer 3",
"Custom Integer 4",
"Custom Integer 5"
        };
    }

    public static class FolderHistory
    {
        static Stack<INEFolder> BackList = new Stack<INEFolder>();
        static Stack<INEFolder> FrontList = new Stack<INEFolder>();
        static INEFolder CurrentFolder;

         public static INEFolder AddInHistory(this INEFolder folder)
        {
            if (CurrentFolder != null && CurrentFolder.UID!=folder.UID)   
            {
                BackList.Push(CurrentFolder);
                FrontList.Clear();
            }
            CurrentFolder = folder;
            return CurrentFolder;
        }

        public static void Back()
        {
            if (CurrentFolder != null && CurrentFolder.UID != CurrentFolder.ParentLibrary.Parent.ActiveFolder.UID)
                CurrentFolder.Activate();
            else if (BackList.Count > 0)
            {
                FrontList.Push(CurrentFolder);
                CurrentFolder = BackList.Pop();
                CurrentFolder.Activate();
            }
        }

        public static void Front()
        {
            if (CurrentFolder != null && CurrentFolder.UID != CurrentFolder.ParentLibrary.Parent.ActiveFolder.UID)
                CurrentFolder.Activate();
            else if (FrontList.Count > 0)
            {
                BackList.Push(CurrentFolder);
                CurrentFolder = FrontList.Pop();
                CurrentFolder.Activate();
            }
        }
    }


}

